/**
 * @file Fault.hh
 * @author Xiaoze Lin (linxiaoze96@gmail.com)
 * @brief The fault classes.
 * @version 0.1
 *
 * @copyright Copyright (c) 2024
 *
 */
#pragma once

#include "NetlistManager.hh"
#include "log/Log.hh"

namespace ifsim {

static std::map<FaultClass, std::string> LUT_FaultClassToString
    = {{EQ, "EQ"},         {UU, "UU"},         {TI, "TI"}, {BL, "BL"}, {RE, "RE"}, {DT, "DT"}, {DI_SCAN, "DI.SCAN"},
       {DI_CLK, "DI.CLK"}, {DI_SEN, "DI.SEC"}, {DS, "DS"}, {UO, "UO"}, {UC, "UC"}, {PT, "PT"}, {AU, "AU"},
       {AU_SEQ, "AU.SEQ"}, {UNKNOWN, "UC"},    {NP, "NP"}, {ND, "ND"}, {EQ, "--"}};

static std::map<std::string, FaultClass> LUT_ToStringFaultClass
    = {{"EQ", EQ},         {"UU", UU},         {"TI", TI}, {"BL", BL}, {"RE", RE}, {"DT", DT}, {"DI.SCAN", DI_SCAN},
       {"DI.CLK", DI_CLK}, {"DI.SEC", DI_SEN}, {"DS", DS}, {"UO", UO}, {"UC", UC}, {"PT", PT}, {"AU", AU},
       {"AU.SEQ", AU_SEQ}, {"UC", UNKNOWN},    {"NP", NP}, {"ND", ND}, {"--", EQ}};

typedef enum
{
  BACKWARD,
  FORWARD,
  CONFLICT
} Direction;

class Fault
{
 public:
  Fault();
  Fault(int fault_id, int gid, char pin_id, FaultType type, FaultClass fault_class);
  Fault(int fault_id, int group_id, int gid, char pin_id, int std_id, char std_pin_id, FaultType type, FaultClass fault_class);
  ~Fault();

  bool operator==(const Fault& p) const { return _gid == p._gid && _pinId == p._pinId && _type == p._type; }

  int faultId() { return _id; }
  void faultId(int id) { _id = id; }

  int groupId() { return _groupId; }
  void groupId(int id) { _groupId = id; }

  int collapsedGroupId() { return _collapsedGroupId; }
  void collapsedGroupId(int id) { _collapsedGroupId = id; }

  int stdId() { return _stdId; }
  void stdId(int std_id) { _stdId = std_id; }

  char stdPinId() { return _stdPinId; }
  void stdPinId(char std_pin_id) { _stdPinId = std_pin_id; }

  int gid() { return _gid; }
  void gid(int gid) { _gid = gid; }

  char pinId() { return _pinId; }
  void pinId(char pin_id) { _pinId = pin_id; }

  FaultType faultType() { return _type; }
  void faultType(FaultType type) { _type = type; }

  FaultClass faultClass() { return _faultClass; }
  void faultClass(FaultClass fault_class) { _faultClass = fault_class; }

 protected:
  int _id;                 // fault id
  int _groupId;            // equivalent group id
  int _collapsedGroupId;   // collapsd group id
  int _stdId;              // std cell id
  char _stdPinId;          // pin id in std cell
  int _gid;                // sim node id
  char _pinId;             // sim node pin id
  FaultType _type;         // fault type
  FaultClass _faultClass;  // fault class
};

class FaultManager
{
 public:
  FaultManager();
  FaultManager(AtpgLibrary* atpg_lib);
  ~FaultManager();

  void identifyFullAndDifferentClassFaults();

  // identify full and equivalent faults
  void identifyFullFaults();
  void identifyEquivalentFaults(std::vector<Fault>& full_fault_list);
  void identifyCollapsedFaults();

  // identify different class faults
  void identifyDifferentClassFaults(const std::vector<Fault>& full_fault_list);
  void identifyUnusedFaults();
  void identifyTiedFaults();
  void identifyBlockedFaults();

  bool readFaultList(const char* fault_list_file);

  int numUntestableFaults();
  int numUnusedFaults();
  int numTiedFaults();
  int numBlockedFaults();

  const std::vector<Fault>& fullFaultList() { return _fullFaultList; }

  const std::vector<std::multimap<char, int>>& simGateFaultList() { return _simGateFaultList; }
  const std::multimap<char, int>& simGateFaultList(int gid) { return _simGateFaultList[gid]; }
  const std::vector<std::vector<Fault>>& equivalentFaultGroup() { return _equivalentFaultGroup; }
  const std::vector<int>& cosntCollapsedFaultGroup() { return _collapsedFaultGroup; }
  const std::vector<std::vector<Fault>>& readFaultList() { return _readFaultList; }

  int equivalentGroupIdToCollapsedGroupId(int group_id) { return _equivalentFaultGroup[group_id][0].collapsedGroupId(); }

  int numFullFaults() { return _numFullFaults; }
  void numFullFaults(int num) { _numFullFaults = num; }

  int numFaultGroups() { return _numFaultGroups; }
  void numFaultGroups(int num) { _numFaultGroups = num; }

  std::vector<int> collapsedFaultGroup() { return _collapsedFaultGroup; }
  void clearCollapsedFaults() { _collapsedFaultGroup.clear(); }
  void addCollapsedFault(int group_id) { _collapsedFaultGroup.push_back(group_id); }
  int numCollapsedFaults() { return _collapsedFaultGroup.size(); }

 private:
  int _numFullFaults;   // num of full faults
  int _numFaultGroups;  // num of equivalent fault groups

  std::vector<Fault> _fullFaultList;                        // full fault list
  std::vector<std::multimap<char, int>> _simGateFaultList;  // fault list of corresponding sim gate
  std::vector<std::vector<Fault>> _equivalentFaultGroup;    // equivalent fault groups
  std::vector<int> _collapsedFaultGroup;                    // collapsed fault groups
  std::vector<std::vector<Fault>> _readFaultList;           // read fault list

  // untestable fault list, ele -> equiv_fault_group_id
  std::set<int> _unusedFaultGroups;   // unused fault
  std::set<int> _tiedFaultGroups;     // tied fault
  std::set<int> _blockedFaultGroups;  // blocked fault

  NetlistManager* _netlistManager;
  AtpgLibrary* _atpgLib;
};
};  // namespace ifsim